package com.zj.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Activate(group = CommonConstants.CONSUMER)
public class TpMonitorFilter implements Filter {
    static Map<String, List<CoastObj>> map = new ConcurrentHashMap<>();
    static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
    static double PERCENT90 = 0.9;
    static double PERCENT99 = 0.99;

    static {
        executor.scheduleAtFixedRate(TpMonitorFilter::report, 5, 5, TimeUnit.SECONDS);
    }

    public static void report() {
        System.out.println("------------------");
        map.entrySet().forEach(entry -> {
            String key = entry.getKey();

            List<CoastObj> value = entry.getValue();

            removeBeforMinuter(value,getOuttime());

            List<Long> collect = value.stream()
                    .map(CoastObj::getSpendTime)
                    .sorted()
                    .collect(Collectors.toList());
            int size = value.size();
            int index90 = (int) (size * PERCENT90);
            int index99 = (int) (size * PERCENT99);
            System.out.print(key);
            System.out.print("   reqCount-->" + size);
            System.out.print("  top90-->" + collect.get(index90));
            System.out.println("  top99-->" + collect.get(index99));

        });
    }

    //获取一分钟以前的时间
    public static long getOuttime(){
        return System.currentTimeMillis()- 60*1000;
    }

    //删除一分钟前的记录
    public static void  removeBeforMinuter(List<CoastObj> list, long timeout) {
        Iterator<CoastObj> it = list.iterator();
        while (it.hasNext()) {
            CoastObj x = it.next();
            if (x.getStartTime() < timeout) {
                it.remove();
            }
        }
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        Result invoke = invoker.invoke(invocation);
        long endTime = System.currentTimeMillis();
        int coast = (int) (endTime - startTime);
        String serviceName = invocation.getServiceName();
        String methodName = invocation.getMethodName();
        String key = serviceName + "." + methodName;

        getCoast(key).add(new CoastObj(startTime,coast));
        return invoke;
    }

    public List<CoastObj> getCoast(String key) {
        return map.computeIfAbsent(key, (k) -> new ArrayList<>());
    }

    class CoastObj {
        public long startTime;
        public long spendTime;

        public CoastObj(long startTime, long spendTime) {
            this.startTime = startTime;
            this.spendTime = spendTime;
        }

        public long getStartTime() {
            return startTime;
        }

        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }

        public long getSpendTime() {
            return spendTime;
        }

        public void setSpendTime(long spendTime) {
            this.spendTime = spendTime;
        }
    }


}
